use std::error::Error;

use chrono::prelude::NaiveTime;
use chrono::{Duration, Timelike};

pub enum CalType {
    ADD,
    SUB,
}

static TIME_M_START: usize = 93000;
static TIME_M_END: usize = 113000;
static TIME_A_START: usize = 130000;
static TIME_A_END: usize = 150000;

/// 产生时间序列的迭代器
pub struct TimeRange {
    pub start: usize,
}
impl Iterator for TimeRange {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        let time = self.start;
        self.start = cal_time_s(self.start, 1, CalType::ADD);
        if in_noon(self.start) {
            self.start = TIME_A_START;
        }

        if !in_interval(self.start) {
            return None;
        }

        Some(time)
    }
}

/// 时间运算（s)，在某个时间加上或者减去固定的秒数
pub fn cal_time_s(current: usize, second: usize, cal_type: CalType) -> usize {
    let cur_str = format!("{:06}", current);
    let cur_time = NaiveTime::parse_from_str(&cur_str, "%H%M%S");
    let res_time = match cal_type {
        CalType::ADD => cur_time.unwrap() + Duration::seconds(second as i64),
        CalType::SUB => cur_time.unwrap() - Duration::seconds(second as i64),
    };

    let res_time_usize = res_time.hour() * 10000 + res_time.minute() * 100 + res_time.second();

    res_time_usize as usize
}

/// 时间运算，毫秒
pub fn cal_time_ms(current: usize, ms: usize, cal_type: CalType) -> usize {
    let cur_str = format!("{:09}", current);
    let cur_time = NaiveTime::parse_from_str(&cur_str, "%H%M%S%3f");
    let res_time = match cal_type {
        CalType::ADD => cur_time.unwrap() + Duration::milliseconds(ms as i64),
        CalType::SUB => cur_time.unwrap() - Duration::milliseconds(ms as i64),
    };

    let res_time_usize = res_time.hour() * 10000000
        + res_time.minute() * 100000
        + res_time.second() * 1000
        + res_time.nanosecond() / 1_000_000;

    res_time_usize as usize
}

/// 计算某个时间十分钟之前的时间
pub fn sub_10_minus(current_time: usize) -> Result<usize, Box<dyn Error>> {
    let result = cal_time_s(current_time, 10 * 60, CalType::SUB);

    use std::cmp::Ordering::Less;

    if in_morning(current_time) {
        match cal_time_s(current_time, 10 * 60, CalType::SUB).cmp(&TIME_M_START) {
            Less => Ok(TIME_M_START),
            _ => Ok(result),
        }
    } else if in_afternoon(current_time) {
        match cal_time_s(current_time, 10 * 60, CalType::SUB).cmp(&TIME_A_START) {
            Less => Ok(TIME_A_START),
            _ => Ok(result),
        }
    } else {
        Err("输入参数错误".into())
    }
}

/// 判断时间是否在开市的区间
pub fn in_interval(current: usize) -> bool {
    in_morning(current) || in_afternoon(current)
}

/// 判断时间是否在早上开市时间
pub fn in_morning(current: usize) -> bool {
    current <= TIME_M_END && current >= TIME_M_START
}

/// 判断时间是否在下午开市时间
pub fn in_afternoon(current: usize) -> bool {
    current <= TIME_A_END && current >= TIME_A_START
}

/// 判断时间是否在中午休市时间
pub fn in_noon(current: usize) -> bool {
    current > TIME_M_END && current < TIME_A_START
}

#[cfg(test)]
mod tests {
    use crate::local_time;

    use super::CalType;

    #[test]
    pub fn test_sub_10_min() {
        let m1 = local_time::sub_10_minus(140922_usize).unwrap();
        assert_eq!(m1, 135922_usize);

        let m1 = local_time::sub_10_minus(93000_usize).unwrap();
        assert_eq!(m1, 93000_usize);

        let m1 = local_time::sub_10_minus(93720_usize).unwrap();
        assert_eq!(m1, 93000_usize);

        let m1 = local_time::sub_10_minus(102939_usize).unwrap();
        assert_eq!(m1, 101939_usize);

        let m1 = local_time::sub_10_minus(130202_usize).unwrap();
        assert_eq!(m1, 130000_usize);
    }

    #[test]
    pub fn test_cal_time_s() {
        let start = 93000_usize;
        let s = 1_usize;
        let result = local_time::cal_time_s(start, s, CalType::ADD);

        assert_eq!(result, 93001);
    }

    #[test]
    pub fn test_cal_time_ms() {
        let start = 93020123_usize;
        let ms = 10123_usize;
        let result = local_time::cal_time_ms(start, ms, CalType::ADD);

        assert_eq!(result, 93030246);
    }
}
